// 缓存管理器

import { BaseManager } from '../core/BaseManager';
import { RedisClient } from '../core/RedisClient';
import { CacheOptions, CacheEntry, CacheStats, CacheInvalidationResult } from '../types/cache';
import { KeyGenerator } from '../utils/keyGenerator';

export class CacheManager extends BaseManager {
  private tagKeyGenerator: KeyGenerator;
  private defaultTtl: number = 3600; // 默认1小时

  constructor(client: RedisClient) {
    super(client, new KeyGenerator({ prefix: 'cache' }));
    this.tagKeyGenerator = new KeyGenerator({ prefix: 'tag' });
  }

  /**
   * 设置缓存
   */
  async set<T>(key: string, value: T, options: CacheOptions = {}): Promise<void> {
    this.validateRequired({ key, value });

    const cacheKey = this.keyGenerator.cache(key);
    const entry: CacheEntry<T> = {
      value,
      createdAt: new Date(),
      expiresAt: options.ttl ? new Date(Date.now() + options.ttl * 1000) : undefined,
      tags: options.tags ?? [],
      hits: 0,
      size: this.calculateSize(value),
    };

    const startTime = this.now();

    try {
      // 设置主缓存
      await this.setValue(cacheKey, entry, options.ttl);

      // 处理标签
      if (options.tags && options.tags.length > 0) {
        await this.setTags(cacheKey, options.tags);
      }

      this.logMetric('cache.set', this.now() - startTime, true, {
        key,
        ttl: options.ttl,
        tags: options.tags,
        size: entry.size,
      });
    } catch (error) {
      this.logMetric('cache.set', this.now() - startTime, false, { key, error });
      throw this.formatError(error, 'Failed to set cache');
    }
  }

  /**
   * 获取缓存
   */
  async get<T>(key: string): Promise<T | null> {
    this.validateRequired({ key });

    const cacheKey = this.keyGenerator.cache(key);
    const startTime = this.now();

    try {
      const entry = await this.getValue<CacheEntry<T>>(cacheKey);

      if (!entry) {
        this.logMetric('cache.get', this.now() - startTime, false, { key, reason: 'miss' });
        return null;
      }

      // 检查是否过期
      if (entry.expiresAt && entry.expiresAt < new Date()) {
        await this.deleteKeys(cacheKey);
        this.logMetric('cache.get', this.now() - startTime, false, { key, reason: 'expired' });
        return null;
      }

      // 更新命中次数
      entry.hits++;
      const currentTtl = await this.getTtl(cacheKey);
      // 只有当TTL大于0时才设置，避免传递-1
      if (currentTtl > 0) {
        await this.setValue(cacheKey, entry, currentTtl);
      } else {
        await this.setValue(cacheKey, entry);
      }

      this.logMetric('cache.get', this.now() - startTime, true, {
        key,
        hits: entry.hits,
        size: entry.size,
      });

      return entry.value;
    } catch (error) {
      this.logMetric('cache.get', this.now() - startTime, false, { key, error });
      throw this.formatError(error, 'Failed to get cache');
    }
  }

  /**
   * 批量获取缓存
   */
  async mget<T>(keys: string[]): Promise<(T | null)[]> {
    this.validateRequired({ keys });

    const results: (T | null)[] = [];
    const pipeline = this.createPipeline();

    // 批量获取
    for (const key of keys) {
      const cacheKey = this.keyGenerator.cache(key);
      pipeline.get(cacheKey);
    }

    const pipelineResults = await pipeline.exec();

    for (let i = 0; i < pipelineResults.length; i++) {
      const [error, result] = pipelineResults[i];

      if (error || !result) {
        results.push(null);
        continue;
      }

      try {
        const entry: CacheEntry<T> = JSON.parse(result as string);

        // 检查是否过期
        if (entry.expiresAt && entry.expiresAt < new Date()) {
          results.push(null);
          continue;
        }

        results.push(entry.value);
      } catch {
        results.push(null);
      }
    }

    return results;
  }

  /**
   * 删除缓存
   */
  async del(key: string): Promise<boolean> {
    this.validateRequired({ key });

    const cacheKey = this.keyGenerator.cache(key);
    const startTime = this.now();

    try {
      // 获取标签信息
      const entry = await this.getValue<CacheEntry>(cacheKey);

      // 删除主缓存
      const deleted = await this.deleteKeys(cacheKey);

      // 清理标签关联
      if (entry?.tags) {
        await this.removeTags(cacheKey, entry.tags);
      }

      this.logMetric('cache.del', this.now() - startTime, deleted > 0, { key });
      return deleted > 0;
    } catch (error) {
      this.logMetric('cache.del', this.now() - startTime, false, { key, error });
      throw this.formatError(error, 'Failed to delete cache');
    }
  }

  /**
   * 检查缓存是否存在
   */
  async exists(key: string): Promise<boolean> {
    this.validateRequired({ key });

    const cacheKey = this.keyGenerator.cache(key);
    const exists = await this.keyExists(cacheKey);
    return exists > 0;
  }

  /**
   * 获取或设置缓存（缓存穿透保护）
   */
  async getOrSet<T>(
    key: string,
    fetcher: () => Promise<T>,
    options: CacheOptions = {}
  ): Promise<T> {
    this.validateRequired({ key, fetcher });

    // 尝试获取缓存
    const cached = await this.get<T>(key);
    if (cached !== null) {
      return cached;
    }

    // 使用分布式锁防止缓存击穿
    const lockKey = this.keyGenerator.generate('lock', key);
    const lockValue = `${Date.now()}-${Math.random()}`;

    try {
      // 尝试获取锁
      const acquired = await this.execute<string>('set', lockKey, lockValue, 'PX', 5000, 'NX');

      if (acquired === 'OK') {
        try {
          // 再次检查缓存（可能在等待锁期间被其他进程设置）
          const cached = await this.get<T>(key);
          if (cached !== null) {
            return cached;
          }

          // 获取数据并设置缓存
          const data = await fetcher();
          await this.set(key, data, options);
          return data;
        } finally {
          // 释放锁
          await this.execute(
            'eval',
            `
            if redis.call("get", KEYS[1]) == ARGV[1] then
              return redis.call("del", KEYS[1])
            else
              return 0
            end
          `,
            1,
            lockKey,
            lockValue
          );
        }
      } else {
        // 等待一段时间后重试
        await this.sleep(100);
        return this.getOrSet(key, fetcher, options);
      }
    } catch (error) {
      throw this.formatError(error, 'Failed to get or set cache');
    }
  }

  /**
   * 设置带标签的缓存
   */
  async setWithTags<T>(key: string, value: T, tags: string[], ttl?: number): Promise<void> {
    const options: CacheOptions = {
      tags,
      ttl: ttl ?? this.defaultTtl,
    };
    await this.set(key, value, options);
  }

  /**
   * 根据标签失效缓存
   */
  async invalidateByTag(tag: string): Promise<CacheInvalidationResult> {
    this.validateRequired({ tag });

    const tagKey = this.tagKeyGenerator.generate(tag);
    const startTime = this.now();
    const result: CacheInvalidationResult = {
      invalidatedKeys: [],
      totalInvalidated: 0,
      errors: [],
    };

    try {
      // 获取所有关联的缓存键
      const cacheKeys = await this.execute<string[]>('smembers', tagKey);

      if (cacheKeys.length === 0) {
        return result;
      }

      // 批量删除缓存
      const pipeline = this.createPipeline();

      for (const cacheKey of cacheKeys) {
        pipeline.del(cacheKey);
      }

      // 删除标签集合
      pipeline.del(tagKey);

      const pipelineResults = await pipeline.exec();

      for (let i = 0; i < cacheKeys.length; i++) {
        const [error] = pipelineResults[i];
        const cacheKey = cacheKeys[i];

        if (error) {
          result.errors.push({
            key: cacheKey!,
            error: error as Error,
          });
        } else {
          result.invalidatedKeys.push(cacheKey!);
          result.totalInvalidated++;
        }
      }

      this.logMetric('cache.invalidateByTag', this.now() - startTime, true, {
        tag,
        invalidated: result.totalInvalidated,
        errors: result.errors.length,
      });

      return result;
    } catch (error) {
      this.logMetric('cache.invalidateByTag', this.now() - startTime, false, { tag, error });
      throw this.formatError(error, 'Failed to invalidate cache by tag');
    }
  }

  /**
   * 获取缓存统计信息
   */
  async getStats(): Promise<CacheStats> {
    const pattern = this.keyGenerator.pattern('*');
    const keys = await this.getKeys(pattern);

    let totalHits = 0;
    let totalMisses = 0;
    let totalMemory = 0;
    let totalKeySize = 0;

    for (const key of keys) {
      try {
        const entry = await this.getValue<CacheEntry>(key);
        if (entry) {
          totalHits += entry.hits;
          totalMemory += entry.size;
          totalKeySize += key.length;
        }
      } catch {
        totalMisses++;
      }
    }

    return {
      hits: totalHits,
      misses: totalMisses,
      hitRate: totalHits + totalMisses > 0 ? totalHits / (totalHits + totalMisses) : 0,
      totalKeys: keys.length,
      totalMemory,
      averageKeySize: keys.length > 0 ? totalKeySize / keys.length : 0,
    };
  }

  /**
   * 清空所有缓存
   */
  async clear(): Promise<number> {
    const pattern = this.keyGenerator.pattern('*');
    const keys = await this.getKeys(pattern);

    if (keys.length === 0) {
      return 0;
    }

    return this.deleteKeys(...keys);
  }

  /**
   * 设置标签关联
   */
  private async setTags(cacheKey: string, tags: string[]): Promise<void> {
    const pipeline = this.createPipeline();

    for (const tag of tags) {
      const tagKey = this.tagKeyGenerator.generate(tag);
      pipeline.sadd(tagKey, cacheKey);
    }

    await pipeline.exec();
  }

  /**
   * 移除标签关联
   */
  private async removeTags(cacheKey: string, tags: string[]): Promise<void> {
    const pipeline = this.createPipeline();

    for (const tag of tags) {
      const tagKey = this.tagKeyGenerator.generate(tag);
      pipeline.srem(tagKey, cacheKey);
    }

    await pipeline.exec();
  }

  /**
   * 计算值的大小
   */
  private calculateSize(value: any): number {
    return Buffer.byteLength(JSON.stringify(value), 'utf8');
  }
}
